package com.mallcloud.mall.app.service.impl;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mallcloud.mall.app.api.entity.dto.AppMmParkingPayInfoDTO;
import com.mallcloud.mall.app.mapper.AppOrderCouponMapper;
import com.mallcloud.mall.app.service.*;
import com.mallcloud.mall.common.core.constant.enums.mall.OrderEnum;
import com.mallcloud.mall.mall.api.entity.MallOrderCouponDetailModel;
import com.mallcloud.mall.mall.api.entity.MallOrderCouponModel;
import com.mallcloud.mall.mall.api.entity.dto.PayParkingFeeDTO;
import com.mallcloud.mall.mall.api.entity.dto.ProPayCoupon;
import com.mallcloud.mall.mall.api.entity.dto.ProPayCouponInfo;
import com.mallcloud.mall.mall.api.entity.dto.ProValueCardRecharge;
import com.mallcloud.mall.mall.service.MallValueCardService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 券订单
 */
@Slf4j
@Service
@AllArgsConstructor
public class AppOrderCouponServiceImpl extends ServiceImpl<AppOrderCouponMapper, MallOrderCouponModel> implements AppOrderCouponService {
    private final AppMmParkingService mmParkingService;
    private final AppPointsCouponService appPointsCouponService;
    private final AppOrderCouponDetailService appOrderCouponDetailService;
    private final AppParkingCouponService appParkingCouponService;
    private final MallValueCardService mallValueCardService;
    //触发补偿的最大重试次数
    private static final Integer reTryVersionNum = 10;

    @Override
    public void updateTryFail(String orderNo, Integer status, String errMsg) {
        this.baseMapper.updateTryFail(orderNo, status, errMsg);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void handleNotifyByWxPay(MallOrderCouponModel orderCoupon) {
        log.info("[{}]订单数据：{}", orderCoupon.getOrderNo(), JSON.toJSONString(orderCoupon));
        if (orderCoupon.getStatus() == OrderEnum.StatusEnum.SUCCESS.getStatus()) {
            log.error("[{}]订单重复回调拦截", orderCoupon.getOrderNo());
            return;
        }
        String orderType = orderCoupon.getOrderType();
        String attach = orderCoupon.getAttach();

        if (OrderEnum.TypeEnum.ORDER_PARK.getType().equals(orderType)) {
            //缴费上报
            PayParkingFeeDTO payParkingFeeDto = JSONUtil.toBean(JSONUtil.parseObj(attach), PayParkingFeeDTO.class);
            mmParkingService.payParkingFee(payParkingFeeDto);
        } else if (OrderEnum.TypeEnum.ORDER_COUPON.getType().equals(orderType)) {
            ProPayCoupon proPayCoupon = JSONUtil.toBean(JSONUtil.parseObj(attach), ProPayCoupon.class);
            //买券
            appPointsCouponService.proPayCoupon(proPayCoupon);
            //保存买券明细
            List<ProPayCouponInfo> couponIdList = proPayCoupon.getBillNo();
            List<MallOrderCouponDetailModel> orderCouponDetails = couponIdList.stream().map(s -> new MallOrderCouponDetailModel(s.getOrderNo(), s.getCouponId())).collect(Collectors.toList());
            Optional.ofNullable(orderCouponDetails).ifPresent(appOrderCouponDetailService::saveBatch);
        } else if (OrderEnum.TypeEnum.POINT_GOODS_COUPON.getType().equals(orderType)) {

        } else if (OrderEnum.TypeEnum.ORDER_VALUE_CARD_RECHARGE.getType().equals(orderType)) {
            // 储值卡充值
            ProValueCardRecharge proValueCardRecharge = JSONUtil.toBean(JSONUtil.parseObj(attach), ProValueCardRecharge.class);
            mallValueCardService.recharge(proValueCardRecharge);
        }

        //更新订单状态
        this.update(new LambdaUpdateWrapper<MallOrderCouponModel>()
                .eq(MallOrderCouponModel::getId, orderCoupon.getId())
                .set(MallOrderCouponModel::getStatus, OrderEnum.StatusEnum.SUCCESS.getStatus())
        );
    }

    @Override
    public Boolean compensate(List<Long> ids) {
        // 查询重试次数大于等于10次的，失败订单
        List<MallOrderCouponModel> orderCoupons = this.list(new LambdaQueryWrapper<MallOrderCouponModel>()
                .in(MallOrderCouponModel::getId, ids)
                .eq(MallOrderCouponModel::getStatus, OrderEnum.StatusEnum.FAIL.getStatus())
                .eq(MallOrderCouponModel::getHasPayed, true)
                .lt(MallOrderCouponModel::getVersion, reTryVersionNum)
        );

        orderCoupons.forEach(x -> {
            try {
                this.handleNotifyByWxPay(x);
            } catch (Exception e) {
                this.updateTryFail(x.getOrderNo(), OrderEnum.StatusEnum.FAIL.getStatus(), e.getMessage());
                log.error("重试补偿异常：" + e.getMessage(), e);
                // TODO 报警
            }
        });
        return Boolean.TRUE;
    }

    @Override
    public Boolean handleTimeOut() {
        // 订单支付超时 创建时间 < now - 5min
        List<MallOrderCouponModel> orderParkings = this.list(new LambdaQueryWrapper<MallOrderCouponModel>()
                .eq(MallOrderCouponModel::getStatus, OrderEnum.StatusEnum.PENDING.getStatus())
                .eq(MallOrderCouponModel::getHasPayed, false)
                .eq(MallOrderCouponModel::getOrderType, OrderEnum.TypeEnum.ORDER_PARK.getType())
                .lt(MallOrderCouponModel::getCreateTime, DateUtil.date(new Date()).offset(DateField.MINUTE, -5))
        );
        orderParkings.forEach(x -> {
            try {
                this.handleTimeOut(x);
            } catch (Exception e) {
                log.error("重试补偿异常：" + e.getMessage(), e);
                // TODO 报警
            }
        });
        return Boolean.TRUE;
    }

    @Override
    public void handleTimeOut(MallOrderCouponModel dto) {
        String attach = dto.getAttach();

        // 券回滚
        AppMmParkingPayInfoDTO payParkingFeeDto = JSONUtil.toBean(JSONUtil.parseObj(attach), AppMmParkingPayInfoDTO.class);
        if (Objects.nonNull(payParkingFeeDto.getProParkingCouponWriteOff())) {
            appParkingCouponService.parkingCouponReversal(payParkingFeeDto.getProParkingCouponWriteOff());
        }
        // 积分回滚
        if (Objects.nonNull(payParkingFeeDto.getParkingPointsPayDiscount())) {
            appParkingCouponService.parkingPointsDiscountReversal(payParkingFeeDto.getParkingPointsPayDiscount());
        }

        // 标记为支付超时
        this.update(new LambdaUpdateWrapper<MallOrderCouponModel>()
                .eq(MallOrderCouponModel::getId, dto.getId())
                .set(MallOrderCouponModel::getStatus, OrderEnum.StatusEnum.PAY_TIMEOUT.getStatus())
        );
    }

    @Override
    public Boolean cancel(MallOrderCouponModel orderModel) {
        return this.update(new LambdaUpdateWrapper<MallOrderCouponModel>()
                .eq(MallOrderCouponModel::getOrderNo, orderModel.getOrderNo())
                .set(MallOrderCouponModel::getStatus, OrderEnum.StatusEnum.PAY_TIMEOUT.getStatus())
                .set(MallOrderCouponModel::getExceptions, orderModel.getExceptions())
        );
    }
}
